package com.talentgame.tankspk.GameCore;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.g3d.Environment;
import com.badlogic.gdx.graphics.g3d.Model;
import com.badlogic.gdx.graphics.g3d.ModelBatch;
import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.physics.bullet.Bullet;
import com.badlogic.gdx.physics.bullet.DebugDrawer;
import com.badlogic.gdx.physics.bullet.collision.ContactListener;
import com.badlogic.gdx.physics.bullet.collision.btBroadphaseInterface;
import com.badlogic.gdx.physics.bullet.collision.btCollisionConfiguration;
import com.badlogic.gdx.physics.bullet.collision.btCollisionDispatcher;
import com.badlogic.gdx.physics.bullet.collision.btDbvtBroadphase;
import com.badlogic.gdx.physics.bullet.collision.btDefaultCollisionConfiguration;
import com.badlogic.gdx.physics.bullet.collision.btDispatcher;
import com.badlogic.gdx.physics.bullet.dynamics.btConstraintSolver;
import com.badlogic.gdx.physics.bullet.dynamics.btDiscreteDynamicsWorld;
import com.badlogic.gdx.physics.bullet.dynamics.btDynamicsWorld;
import com.badlogic.gdx.physics.bullet.dynamics.btSequentialImpulseConstraintSolver;
import com.badlogic.gdx.physics.bullet.linearmath.btIDebugDraw;
import com.badlogic.gdx.physics.bullet.linearmath.btMotionState;
import com.badlogic.gdx.utils.Json;
import com.badlogic.gdx.utils.JsonWriter;
import com.badlogic.gdx.utils.StringBuilder;
import com.talentgame.tankspk.GameNet.DTO.MatchSuccessDto;
import com.talentgame.tankspk.GameNet.DTO.PlayerHitDto;
import com.talentgame.tankspk.GameNet.DTO.PlayerModel;
import com.talentgame.tankspk.GameNet.ProtocolConst;
import com.talentgame.tankspk.GameNet.RequestPackage;
import com.talentgame.tankspk.GameNet.SocketClient;
import com.talentgame.tankspk.UIStage.GameOverStage;
import com.talentgame.tankspk.UIStage.GameStage;
import com.talentgame.tankspk.UIStage.StartStage;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;

/**
 * Created by 54560 on 2016/12/18.
 */
public class TanksGame implements ApplicationListener {
    public static final int GAME_GameStartMenu =1;
    public static final int GAME_Gaming = 2;
    public static final int GAME_Matching =3; //匹配中
    public static final int GAME_OVER =4;//结算界面
    //游戏当前状态
    public int gameState = GAME_GameStartMenu;

    public static TanksGame instance;


    final static short PLAYER_FLAG = 1 << 8;
    final static short OBJECT_FLAG = 1 << 9;
    final static short ALL_FLAG = -1;

    FollowPlayerCam followCam;

    HashMap<Integer,GameObject> renderList =new HashMap<Integer,GameObject>();
    HashMap<Integer,Tank> playersTanks = new HashMap<>();
    GameStage gameStage;
    StartStage startStage;
    GameOverStage gameOverStage;

    Player player;
    ModelBatch mBatch;
    Environment environment;
    AssetManager assets;
    boolean isLoading;
    Sound shotSound;
    Sound playerExplosionSound;
    Sound bgSound;
    Sound bullethitSound;
    Sound bulletExplosionSound;

    SocketClient client;
    int playerid= -1;
    Json jsonconvert;

    btCollisionConfiguration collisionConfig;
    btDispatcher dispatcher;
    MyContactListener contactListener;
    btBroadphaseInterface broadphase;
    btDynamicsWorld collisionWorld;
    btConstraintSolver constraintSolver;
    DebugDrawer debugDrawer = null;
    int debugMode = btIDebugDraw.DebugDrawModes.DBG_NoDebug;
    int UId;
    class MyContactListener extends ContactListener {
        @Override
        public boolean onContactAdded(int userValue0, int partId0, int index0, int userValue1, int partId1, int index1) {
            GameObject go1;
            GameObject go2;
            if(!TanksGame.instance.renderList.containsKey(userValue1) || !TanksGame.instance.renderList.containsKey(userValue0))
            {
                Gdx.app.log("onContact : ",userValue0+" 不存在,或者 :  " + userValue1 + " 不存在");
                return false;
            }
            go1=TanksGame.instance.renderList.get(userValue0);
            go2=TanksGame.instance.renderList.get(userValue1);
            //如果打到了墙上了
            if(go2.nodes.get(0).id .equals("Shell") )
            {
                //播放子弹音效
                bulletExplosionSound.play();

                //如果自己被打到了
                if(go1.uniqueId == player.tank.uniqueId)
                {
                    PlayerHitDto dto = new PlayerHitDto();
                    Tank tank = (Tank) go1;
                    TankBullet tb = (TankBullet)go2;
                    dto.fromPlayerid = tb.playerid;
                    dto.playerid = playerid;
                    String json = jsonconvert.toJson(dto);
                    client.sendRequest(new RequestPackage(ProtocolConst.PlayerHit,json));
                    player.Hp -= 10;
                    Gdx.app.log("GetHurt","伤害");
                }
                GameObject.Destory(go2);
            }
            return true;
        }
    }

    @Override
    public void create() {
        instance = this;
        assets = new AssetManager();

        jsonconvert = new Json(JsonWriter.OutputType.json);

        //设置主界面的UI
        gameStage = new GameStage(assets);
        startStage = new StartStage(assets);
        gameOverStage = new GameOverStage(assets);
        Gdx.input.setInputProcessor(startStage);
        //Gdx.input.setInputProcessor(gameStage);
        mBatch = new ModelBatch();
        environment = new Environment();
        environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));
        environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f));

        //跟随摄像机
        followCam = new FollowPlayerCam();

        assets.load("Model/tankScenes.g3dj",Model.class);

        assets.load("audio/BattleMusic.wav",Sound.class);
        assets.load("audio/BulletExplosion.mp3",Sound.class);
        assets.load("audio/BulletHit.mp3",Sound.class);
        assets.load("audio/PlayerExplosion.mp3",Sound.class);
        assets.load("audio/PlayerShot.mp3",Sound.class);
        isLoading = true;
        //阻塞读取
        assets.finishLoading();

        Bullet.init();
        collisionConfig = new btDefaultCollisionConfiguration();
        dispatcher = new btCollisionDispatcher(collisionConfig);
        broadphase = new btDbvtBroadphase();
        constraintSolver = new btSequentialImpulseConstraintSolver();
        collisionWorld = new btDiscreteDynamicsWorld(dispatcher, broadphase, constraintSolver,collisionConfig);
        contactListener = new MyContactListener();
        collisionWorld.setGravity(new Vector3(0, -10, 0));
        collisionWorld.setDebugDrawer(debugDrawer = new DebugDrawer());
        debugDrawer.setDebugMode(debugMode);
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        client.close();
        Gdx.app.log("finalize","退出游戏了");
    }

    void doLoading()
    {
        bgSound = assets.get("audio/BattleMusic.wav",Sound.class);
        bulletExplosionSound = assets.get("audio/BulletExplosion.mp3",Sound.class);
        bullethitSound =  assets.get("audio/BulletHit.mp3",Sound.class);
        playerExplosionSound = assets.get("audio/PlayerExplosion.mp3",Sound.class);
        shotSound = assets.get("audio/PlayerShot.mp3",Sound.class);



        Model scenes = assets.get("Model/tankScenes.g3dj",Model.class);
        for (int i = 0; i < scenes.nodes.size; i++) {
            String id = scenes.nodes.get(i).id;
            GameObject instance=null;
            //碰撞和 地板
            if(id.contains("Colider") | id.contains("Plane"))
            {
                instance = new Obstacle(scenes,id);
                GameObject.instantiate(instance,true);
            }
            //坦克
            else if(id.contains("TanksScene:Tank"))
            {


            }
            //子弹
            else if(id.contains("Shell"))
            {

            }
            //其他场景物体
            else
            {
                instance = new GameObject(scenes, id);
                GameObject.instantiate(instance,false);
            }
            if(instance!=null)
            {
                renderList.put(instance.uniqueId,instance);
            }

        }

        isLoading = false;
    }

    public void StartGame()
    {
        //gameState = GAME_Matching;
        ChangeState(GAME_Matching);
        client = new SocketClient();
        try {
            client.start();

            RequestPackage rq = new RequestPackage();
            rq.cmd = ProtocolConst.MatchGame;
            client.sendRequest(rq);
        } catch (IOException e) {
            Gdx.app.log("Client",e.getMessage());
            client.close();
            //gameState = GAME_GameStartMenu;
            ChangeState(GAME_GameStartMenu);
        }
    }
    public void ChangeState(int state)
    {
        gameState = state;
        if(gameState == GAME_OVER)
        {
            Gdx.input.setInputProcessor(gameOverStage);
            for(Tank go : playersTanks.values())
            {
                GameObject.Destory(go);
            }
            playersTanks.clear();
            gameOverStage.score = player.score;
            gameOverStage.scoreLabel.setText("You Kill :" + player.score + " Tanks");
            player=null;
            client.close();
            //collisionWorld = collisionWorld.removeRigidBody();
        }else if(gameState == GAME_GameStartMenu)
        {
            Gdx.input.setInputProcessor(startStage);
        }else  if(gameState == GAME_Gaming)
        {
            Gdx.input.setInputProcessor(gameStage);
        }
    }
    public void BackToMenu()
    {
        ChangeState(GAME_GameStartMenu);
    }
    @Override
    public void resize(int width, int height) {

    }

    //处理网络信息队列
    public void recQueue()
    {
        if(client == null)return;
        //for(int i=client.recQueue.size();i>=0;i--)
        //{
            while (client.recQueue.size() > 0)
            {
                RequestPackage rq = client.recQueue.poll();
            if(rq == null) return;
                String j = rq.json;
            if(j ==null)
                Gdx.app.log("正在处理消息","rp : " + rq.cmd);
            else
                Gdx.app.log("正在处理消息","rp : " + rq.cmd + "   json " + rq.json);
                if(rq.cmd == ProtocolConst.MatchGame)
                {
                    EnterGameScene(rq);
                }else if(rq.cmd == ProtocolConst.OtherEnter)
                {
                    PlayerModel model = jsonconvert.fromJson(PlayerModel.class,rq.json);
                    Tank tank = SpwanTank(model.playerid);
                    GameObject.instantiate(tank,true);
                    playersTanks.put(model.playerid,tank);
                    tank.GetPlayerModel().playerid=model.playerid;
                    //tank.Sync(model);
                }
                else if(rq.cmd == ProtocolConst.OtherLeave)
                {
                    //PlayerModel model = jsonconvert.fromJson(PlayerModel.class,rq.json);
                    int playerid = Integer.parseInt( rq.json);
                    if(playersTanks.containsKey(playerid))
                    {
                        Tank tank = playersTanks.get(playerid);
                        GameObject.Destory(tank);
                        playersTanks.remove(playerid);
                    }
                }
                else if(rq.cmd == ProtocolConst.PlayerSync)
                {
                    PlayerModel model = jsonconvert.fromJson(PlayerModel.class,rq.json);
                    if(playersTanks.containsKey(model.playerid))
                    {
                        Tank tank = playersTanks.get(model.playerid);
                        tank.Sync(model);
                    }
                }else if(rq.cmd == ProtocolConst.PlayerDead)//人物死亡
                {
                    playerExplosionSound.play();
                    PlayerHitDto dto = jsonconvert.fromJson(PlayerHitDto.class,rq.json);

                    if(playersTanks.containsKey(dto.playerid))
                    {
                        Tank tank = playersTanks.get(dto.playerid);
                        GameObject.Destory(tank);
                        playersTanks.remove(dto.playerid);
                        //如果是自己击杀就加一分~~本来应该服务器做的额。。偷懒
                        if(dto.fromPlayerid == playerid)
                        {
                            player.score ++;
                        }

                        //如果死的是自己~
                        if(dto.playerid == playerid)
                        {
                            //游戏结束
                            //gameState = GAME_OVER;
                            //Gdx.input.setInputProcessor(gameOverStage);
                            ChangeState(GAME_OVER);

                        }
                    }
                }
            }
        //}
    }
    void EnterGameScene(RequestPackage rq)
    {
        //bgSound.play();
        bgSound.loop();
        MatchSuccessDto dto = jsonconvert.fromJson(MatchSuccessDto.class,rq.json);
        this.playerid = dto.playerId; //匹配成功会返回自己的playerid

        Model scenes = assets.get("Model/tankScenes.g3dj",Model.class);
        Tank playerTank = SpwanTank(playerid);
        GameObject.instantiate(playerTank,true);
        player = new Player(playerTank,gameStage.touchPadLeft,gameStage.touchPadRight);
        playersTanks.put(this.playerid,playerTank);

        for (PlayerModel model :dto.players) {

            Tank tank = SpwanTank(model.playerid);
            GameObject.instantiate(tank,true);
            playersTanks.put(model.playerid,tank);
            tank.GetPlayerModel().playerid=model.playerid;
            tank.SetSync(model);
        }
        followCam.LookTarget(player.tank);
//        gameState = GAME_Gaming;
//        Gdx.input.setInputProcessor(gameStage );
        ChangeState(GAME_Gaming);
        Gdx.app.log("S","匹配成功加入游戏！");
    }

    /*
    * 生成坦克
    * */
    Tank SpwanTank(int playerid)
    {
        Model scenes = assets.get("Model/tankScenes.g3dj",Model.class);
        Tank tank = new Tank(scenes,"TanksScene:Tank","Shell",playerid);
        return  tank;
    }
    //逻辑
    public void update(float detaltime)
    {
        if(!assets.update() && isLoading)
            return;

        recQueue();

        if(gameState == GAME_GameStartMenu)
        {

        }
        else if(gameState == GAME_Gaming)
        {
            ArrayList<GameObject> tempList = new ArrayList<GameObject>();
            tempList.addAll(renderList.values());
            for(GameObject go :tempList)
            {
                go.update(detaltime);
            }
            if(player != null)
            {
                player.update(detaltime);
                followCam.update(detaltime);
                collisionWorld.stepSimulation(detaltime,5,1/60f);
            }
        }else if(gameState == GAME_Matching)
        {

        }
        else if(gameState == GAME_OVER)
        {

        }


//        TestDto dto = new TestDto(i,"hehe","haha");
//        Json json = new Json(JsonWriter.OutputType.json);
//        RequestPackage rp = new RequestPackage(ProtocolConst.TestSend,json.toJson(dto,TestDto.class));
//        client.sendRequest(rp);
        //collisionWorld.stepSimulation(detaltime, 5, 1f / 60f);

    }
    //真是渲染
    public void realRender()
    {
        Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
        //背景颜色
        Gdx.gl.glClearColor(50.0f/255.0f,50.0f/255.0f,50.0f/255.0f,1);

        if(gameState == GAME_GameStartMenu)
        {
            startStage.act(Gdx.graphics.getDeltaTime());
            startStage.draw();
        }
        else if(gameState == GAME_Gaming)
        {

            mBatch.begin(followCam.Cam);
            mBatch.render(renderList.values(),environment);
            mBatch.end();
            if (debugDrawer != null && debugDrawer.getDebugMode() > 0) {
                mBatch.flush();
                debugDrawer.begin(followCam.Cam);
                collisionWorld.debugDrawWorld();
                debugDrawer.end();
            }
            gameStage.act(Gdx.graphics.getDeltaTime());
            gameStage.draw();
        }else if(gameState == GAME_Matching)
        {

        }else if(gameState == GAME_OVER)
        {
            gameOverStage.act(Gdx.graphics.getDeltaTime());
            gameOverStage.draw();
        }


    }
    @Override
    public void render() {
        final float delta = Math.min(1f / 30f, Gdx.graphics.getDeltaTime());
        //需要等待所有加载完毕
        if(isLoading && assets.update())
            doLoading();

        update(delta);
        realRender();
    }
    @Override
    public void pause() {

    }

    @Override
    public void resume() {

    }

    @Override
    public void dispose() {
        assets.dispose();
        for(GameObject go : renderList.values())
            go.dispose();
        renderList.clear();
        mBatch.dispose();

        debugDrawer.dispose();
        constraintSolver.dispose();
        collisionWorld.dispose();
        broadphase.dispose();
        dispatcher.dispose();
        collisionConfig.dispose();
        contactListener.dispose();
        client.close();
    }
}
